package com.yj.rselasticsearch.api;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yj.common.constant.Constants;
import com.yj.common.constant.HttpStatus;
import com.yj.common.core.controller.BaseController;
import com.yj.common.core.domain.AjaxResult;
import com.yj.common.core.domain.entity.Member;
import com.yj.common.core.domain.entity.SysUser;
import com.yj.common.exception.ServiceException;
import com.yj.common.utils.FastUtils;
import com.yj.common.utils.MessageUtils;
import com.yj.common.utils.StringUtils;
import com.yj.common.utils.WechatUtil;
import com.yj.common.utils.bean.BeanUtils;
import com.yj.rselasticsearch.domain.vo.MemberVo;
import com.yj.rselasticsearch.mapper.AsyncManager;
import com.yj.rselasticsearch.mapper.factory.AsyncFactory;
import com.yj.rselasticsearch.service.MemberService;
import com.yj.system.service.ISysUserService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import com.yj.rselasticsearch.service.TokenService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import java.util.Date;

/**
 * 微信登录
 *
 * @author luoy
 */
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/app")
public class AuthController extends BaseController {
    @Autowired
    private MemberService memberService;

    @Resource
    private ISysUserService userService;

    @Resource
    private TokenService tokenService;

    /**
     * 微信小程序登录
     *
     * @return
     */
    @PostMapping("/wx/login")
    public AjaxResult wxLogin(@RequestParam(value = "code") String code) {
        // 用户非敏感信息：rawData
//        // 签名：signature
//        JSONObject rawDataJson = JSON.parseObject(rawData);
        // 1.接收小程序发送的code
        // 2.开发者服务器 登录凭证校验接口 appi + appsecret + code
        JSONObject SessionKeyOpenId = WechatUtil.getSessionKeyOrOpenId(code);
        log.info(SessionKeyOpenId.toJSONString());
        // 3.接收微信接口服务 获取返回的参数
        String sessionKey = SessionKeyOpenId.getString("session_key");
        String unionid = SessionKeyOpenId.getString("unionid");
        String openid = SessionKeyOpenId.getString("openid");
        // 4.校验签名 小程序发送的签名signature与服务器端生成的签名signature2 = sha1(rawData + sessionKey)
//        String signature2 = DigestUtils.sha1Hex(rawData + sessionKey);
//        if (!signature.equals(signature2)) {
//            return AjaxResult.error("签名校验失败");
//        }
        // 5.根据返回的Member实体类，判断用户是否是新用户，是的话，将用户信息存到数据库；
        LambdaQueryWrapper<Member> lqw = Wrappers.lambdaQuery();
        lqw.eq(Member::getOpenId, openid);
        Member member = memberService.getOne(lqw);
        if (member == null) {
            // 用户信息入库
            member = new Member();
            member.setUnionId(unionid);
            member.setOpenId(openid);
            member.setCreateTime(new Date());
            memberService.insertSelective(member);
        }
        if (StringUtils.isNotNull(member.getPhone())) {
            SysUser sysUser = userService.selectUserAuthByPhone(member.getPhone());
            if (!FastUtils.checkNull(sysUser)) {
                MemberVo memberVo = new MemberVo();
                BeanUtils.copyBeanProp(memberVo, member);
                String token = tokenService.createToken(memberVo);
                memberVo.setToken(token);
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(memberVo.getOpenId(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
                return AjaxResult.success("登录成功", memberVo);
            } else {
                return AjaxResult.error(HttpStatus.UNAUTHORIZED, "权限不足，请联系管理员！");
            }
        } else {
            return AjaxResult.error(HttpStatus.UNAUTHORIZED, "未获取到手机号码信息，无法登录！");
        }
    }

    /**
     * 获取用户手机号
     *
     * @param code
     * @param encryptedData
     * @param iv
     * @return
     */
    @PostMapping("/wx/phone")
    public AjaxResult getPhone(@RequestParam(value = "code") String code,
                               @RequestParam(value = "encryptedData") String encryptedData,
                               @RequestParam(value = "iv") String iv) {

        try {
            // 1.接收小程序发送的code
            // 2.开发者服务器 登录凭证校验接口 appi + appsecret + code
            log.info(String.format("微信登录：%s", code));
            JSONObject SessionKeyOpenId = WechatUtil.getSessionKeyOrOpenId(code);
            // 3.接收微信接口服务 获取返回的参数
            String sessionKey = SessionKeyOpenId.getString("session_key");
            String unionid = SessionKeyOpenId.getString("unionid");
            String openid = SessionKeyOpenId.getString("openid");
            log.info(String.format("获取到的微信信息:unionId----%s", unionid));
            log.info(String.format("获取到的微信信息:openId----%s", openid));
            String result = deciphering(encryptedData, sessionKey, iv);
            JSONObject phoneInfo = JSON.parseObject(result);
            String phoneNumber = phoneInfo.getString("phoneNumber");
            if (openid == null || phoneNumber == null) {
                return AjaxResult.error("获取手机号失败");
            }
            // 储存手机号
            LambdaUpdateWrapper<Member> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
            lambdaUpdateWrapper.eq(Member::getOpenId, openid).set(Member::getPhone, phoneNumber);
            memberService.update(lambdaUpdateWrapper);
            Member member = memberService.getOne(new LambdaQueryWrapper<Member>().eq(Member::getOpenId, openid));
            SysUser sysUser = userService.selectUserAuthByPhone(member.getPhone());
            if (!FastUtils.checkNull(sysUser)) {
                MemberVo memberVo = new MemberVo();
                BeanUtils.copyBeanProp(memberVo, member);
                String token = tokenService.createToken(memberVo);
                memberVo.setToken(token);
                return AjaxResult.success("获取成功", memberVo);
            } else {
                return AjaxResult.error(HttpStatus.UNAUTHORIZED, "权限不足，请联系管理员授权！");
            }

        } catch (Exception exception) {
            exception.printStackTrace();
            log.info(exception.getMessage());
        }
        return AjaxResult.error();
    }

    /**
     * 微信退出登录
     * @return
     */
    @PostMapping("/wx/loginOut")
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult loginOut(HttpServletRequest request){
        MemberVo user = (MemberVo)tokenService.getLoginUser(request);
        if (!FastUtils.checkNull(user)){
           boolean result = memberService.update(new LambdaUpdateWrapper<Member>().eq(Member::getOpenId,user.getOpenId()).set(Member::getPhone,null));
           if (result){
               //删除token
               tokenService.delLoginUser(user.getToken());
               // 记录用户退出日志
               AsyncManager.me().execute(AsyncFactory.recordLogininfor(user.getPhone(), Constants.LOGOUT, "退出成功"));
               return AjaxResult.success("退出登录成功！");
           }
        }
        return AjaxResult.success("操作异常，退出登录失败！");
    }

    /**
     * 用户信息解密
     * @param encryptedData 加密数据
     * @param sessionKey
     * @param ivdata 解密iv向量
     * @return
     * @throws Exception
     */
    private String deciphering(String encryptedData, String sessionKey, String ivdata) throws Exception {
        byte[] encrypData = org.apache.commons.codec.binary.Base64.decodeBase64(encryptedData);
        byte[] ivData = org.apache.commons.codec.binary.Base64.decodeBase64(ivdata);
        byte[] sKey = Base64.decodeBase64(sessionKey);
        log.info(String.format("微信解密参数：encryptedData--%s", encryptedData));
        log.info(String.format("微信解密参数：sessionKey--%s", sessionKey));
        log.info(String.format("微信解密参数：ivData--%s", ivData));
        String decrypt = decrypt(sKey, ivData, encrypData);
        log.info(String.format("微信手机号登录获取到的解密信息：%s", decrypt));
        return decrypt;

    }

    /**
     * ASE解密
     * @param key
     * @param iv
     * @param encData
     * @return
     * @throws Exception
     */
    private static String decrypt(byte[] key, byte[] iv, byte[] encData) throws Exception {
        try {
            int base = 16;
            if (key.length % base != 0) {
                int groups = key.length / base + (key.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(key, 0, temp, 0, key.length);
                key = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(key, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(iv));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
            byte[] resultByte = cipher.doFinal(encData);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                return result;
            }
        } catch (Exception e) {
            e.printStackTrace();
            try {


                /**
                 * Exception:javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
                 *偶发性异常：没登录时用AES/CBC/PKCS7Padding，登录后用AES/CBC/PKCS5Padding
                 */
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
                SecretKeySpec spec = new SecretKeySpec(key, "AES");
                AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
                parameters.init(new IvParameterSpec(iv));
                cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
                byte[] resultByte = cipher.doFinal(encData);
                if (null != resultByte && resultByte.length > 0) {
                    String result = new String(resultByte, "UTF-8");
                    return result;
                }
            } catch (Exception ex) {
                ex.printStackTrace();
                throw new ServiceException("登录信息异常，请重新登录！",HttpStatus.ERROR);
            }
            e.printStackTrace();
        }
        return null;
    }



}
